<?php

namespace UU\Suite\DingTalk;

use GuzzleHttp\Client;
use UU\Suite\DingTalk\Kernel\Encryptor;
use UU\Suite\Kernel\Contracts\AccessTokenInterface;
use UU\Suite\Kernel\Contracts\AccountInterface;
use UU\Suite\Kernel\Contracts\ApplicationInterface;
use UU\Suite\Kernel\Contracts\EncryptorInterface;
use UU\Suite\Kernel\Contracts\HttpClientInterface;
use UU\Suite\Kernel\Contracts\ServerInterface;
use UU\Suite\Kernel\HttpClient;
use UU\Suite\Kernel\Traits\InteractsWithCache;
use UU\Suite\Kernel\Traits\InteractWithClient;
use UU\Suite\Kernel\Traits\InteractWithConfig;
use UU\Suite\Kernel\Traits\InteractWithHttpClient;
use UU\Suite\Kernel\Traits\InteractWithServerRequest;

class Application implements ApplicationInterface
{

    use InteractWithConfig;
    use InteractsWithCache;
    use InteractWithHttpClient;
    use InteractWithServerRequest;
    use InteractWithClient;


    protected ?AccessTokenInterface $accessToken = null;

    /**
     * @var Encryptor|null
     */
    protected $encryptor = null;
    /**
     * @var ServerInterface|null
     */
    protected $server = null;

    /**
     * @var AccountInterface
     */
    protected $account = null;

    public function getAccount(): AccountInterface
    {
        if (!$this->account) {
            $this->account = new Account(
                $this->config->get('app_id'),
                $this->config->get('secret'),
                $this->config->get('token'),
                $this->config->get('aes_key'),
            );
        }
        return $this->account;
    }

    public function setAccount(AccountInterface $account): self
    {
        $this->account = $account;

        return $this;
    }

    public function getEncryptor(): EncryptorInterface
    {
        if (!$this->encryptor) {
            $this->encryptor = new Encryptor(
                $this->getAccount()->getAppId(),
                $this->getAccount()->getToken(),
                $this->getAccount()->getAesKey(),
            );
        }
        return $this->encryptor;
    }

    public function setEncryptor(EncryptorInterface $encryptor): Application
    {
        $this->encryptor = $encryptor;
        return $this;
    }

    public function getAccessToken(): AccessTokenInterface
    {
        if (!$this->accessToken) {

            $this->accessToken = new AccessToken(
                $this->getAccount()->getAppId(),
                $this->getAccount()->getSecret(),
                $this->getCache(),
                $this->getHttpClient());
        }

        return $this->accessToken;
    }

    public function setAccessToken(AccessTokenInterface $accessToken): self
    {
        $this->accessToken = $accessToken;

        return $this;
    }

    /**
     * @return ServerInterface
     * @throws \Throwable
     */
    public function getServer(): ServerInterface
    {
        if (!$this->server) {
            $this->server = new Server(
                $this->getRequest(),
                $this->getEncryptor()
            );
        }
        return $this->server;
    }

    public function setServer(ServerInterface $server): self
    {
        $this->server = $server;

        return $this;
    }


    public function createClient(): HttpClientInterface
    {
        return new HttpClient(new Client($this->getHttpClientDefaultOptions()), '/', $this->getAccessToken());
    }

    public function getProviderAccessToken(): AccessTokenInterface
    {
        // TODO: Implement getProviderAccessToken() method.
    }


    public function getHttpClientDefaultOptions(): array
    {
        return \array_merge(
            ['base_uri' => 'https://oapi.dingtalk.com',],
            (array)$this->config->get('http', [])
        );
    }

    public function getDepartment()
    {
        return new Department($this->getClient());
    }

    public function getUser()
    {
        return new User($this->getClient());
    }

}